﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using System.Text;
using System.IO;

namespace BullShitPokerServer
{
    class Server : IServer
    {
        public IGame game;
        
        bool isServerRunning;
        // Это сокет нашего сервера
        Socket listener;
        TcpListener Listener;
        // Порт, на котором будем прослушивать входящие соединения
        int port = 5555;
        // Точка для прослушки входящих соединений (состоит из адреса и порта)
        IPEndPoint Point;
        // Список потоков
        private List<Thread> threads = new List<Thread>();
        private List<Socket> clients = new List<Socket>();
        private Dictionary<Socket, string> names = new Dictionary<Socket, string>();

        public Server(int port)
        {
            this.port = port;
            game = new Game(this.Send, getClients);
            ServerStart();
            MessageReciver();
            CheckSokets();
        }

        public List<Socket> getClients()
        {
            return clients;
        }

        private void ServerStart()
        {
            isServerRunning = true;
            listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            // Определяем конечную точку, IPAddress.Any означает что наш сервер будет принимать входящие соединения с любых адресов
            Point = new IPEndPoint(IPAddress.Any, port);
            // Связываем сокет с конечной точкой
            listener.Bind(Point);
            // Начинаем слушать входящие соединения
            listener.Listen(10);
            SocketAccepter();
        }

        private void SocketAccepter()
        {
            // Запускаем цикл в отдельном потоке, чтобы приложение не зависло
            Thread th = new Thread(delegate()
            {
                while (isServerRunning)
                {
                    // Создаем новый сокет, по которому мы сможем обращаться клиенту
                    // Этот цикл остановится, пока какой-нибудь клиент не попытается присоединиться к серверу
                    Socket client = listener.Accept();
                    client.NoDelay = true;
                    // Теперь, обратившись к объекту client, мы сможем отсылать и принимать пакеты от последнего подключившегося пользователя.
                    // Добавляем подключенного клиента в список всех клиентов, для дальнейшей массовой рассылки пакетов
                    clients.Add(client);
                    byte[] bytes = new byte[100];
                    client.Receive(bytes);
                    string name = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
                    if (name.IndexOf('\0') > 0) name = name.Remove(name.IndexOf('\0'));
                    if (!names.ContainsValue(name))
                    {
                        names.Add(client, name);
                        Console.WriteLine("Player " + name + " was added!");
                    }
                    // Начинаем принимать входящие пакеты
                    /*Thread thh = new Thread(delegate()
                    {
                        MessageReceiver(client);
                    });
                    thh.Start();
                     */
                }
            });
            // Приведенный выше цикл пока что не работает, запускаем поток. Теперь цикл работает.
            th.Start();
            threads.Add(th);
        }
        private void MessageReciver()
        {
            Thread th = new Thread(delegate()
            {
                int Port = port + 1;
                Listener = new TcpListener(IPAddress.Any, Port); // Создаем "слушателя" для указанного порта
                Listener.Start(); // Запускаем его

                // В бесконечном цикле
                while (true)
                {
                    // Принимаем нового клиента
                    TcpClient Client = Listener.AcceptTcpClient();
                    // Создаем поток
                    Thread Thread = new Thread(new ParameterizedThreadStart(ClientThread));
                    // И запускаем этот поток, передавая ему принятого клиента
                    Thread.Start(Client);
                }
            });
            th.Start();
        }

        private void ClientThread(Object StateInfo)
        {
            // Просто создаем новый экземпляр класса Client и передаем ему приведенный к классу TcpClient объект StateInfo
            new Client((TcpClient)StateInfo, game);
        }

        private void CheckSokets()
        {
            Thread th = new Thread(delegate()
            {
                while (isServerRunning)
                {
                    lock (names)
                    {
                        List<Socket> toRemove = new List<Socket>();
                        foreach (Socket s in names.Keys)
                        {
                            if (!s.Connected)
                            {
                                game.RemovePlayer(names[s]);
                                toRemove.Add(s);
                            }
                        }
                        foreach (Socket s in toRemove)
                        {
                            try
                            {
                                clients.Remove(s);
                                names.Remove(s);
                            }
                            catch { }
                        }
                        if (clients.Count < 2 && game.Started)
                        {
                            Send("StopServer");
                            Console.WriteLine("Server Stop");
                        }

                    }
                    Thread.Sleep(1000);
                }
            });
            th.Start();
        }
                
        /*
        private void MessageReceiver(Socket r_client)
        {
            // Для каждого нового подключения, будет создан свой поток для приема пакетов
            Thread th = new Thread(delegate()
            {
                while (isServerRunning)
                {
                    try
                    {
                        // Сюда будем записывать принятые байты
                        byte[] bytes = new byte[1024];
                        // Принимаем
                        r_client.Receive(bytes);
                        string Request = Encoding.UTF8.GetString(bytes, 0, bytes.Length);
                        if (Request.IndexOf('\0') > 0) Request = Request.Remove(Request.IndexOf('\0'));
                        if (Request.Length != 0)
                        {
                            // Отсылаем принятый пакет от клиента всем клиентам
                            /* foreach (Socket s_client in clients.Keys)
                             {
                                 MessageSender(s_client, bytes);
                             }
                                Request = Request.Replace('\a', '\n');
                                string Result = RequestHandler.Request(Request, game, r_client);
                                Result = ':' + Result;
                                MessageSender(r_client, Result);
                        }
                    }
                    catch { }
                }
            });
            th.Start();
            threads.Add(th);
        }
        */

        private void MessageSender(Socket c_client, string msg)
        {
            try
            {
                // Отправляем пакет
                msg = msg.Replace('\n', '\a') + '\n';
                c_client.Send(Encoding.UTF8.GetBytes(msg));
            }
            catch { }
        }

        public void Send(string msg)
        {
            foreach (Socket s_client in clients)
            {
                MessageSender(s_client, msg);
            }
        }

        // Остановка сервера
        ~Server()
        {
            // Если "слушатель" был создан
           
        }
    }
}
